from typing import Optional, List, Tuple

def count_leading_whitespace(s: str) -> int:
    """
    Count the number of leading whitespace characters in a string.
    """
    return len(s) - len(s.lstrip())

def get_up_arrow_line(lines: List[str]) -> Optional[str]:
    """
    Get the line containing the up arrow from a list of lines.
    For example:
    4 |     println("Hello, world!");
      |     ^^^^^^^ not a function  <- this line
    """
    for line in lines:
        if "^" in line:
            return line
    return None

def get_up_arrow_start_end_pos(line: str) -> Tuple[int, int]:
    """
    Get the start and end positions of the up arrow in a line.
    For example:
    4 |     println("Hello, world!");
      |     ^^^^^^^ not a function
    The start position is the position of 'p' and the end position is the position of 'n'.
    """
    if "| " in line:
        s = line.index("| ")
        line = line[s + len("| "):]

    first_caret = line.find('^')
    last_caret = line.rfind('^')
    return first_caret, last_caret + 1

def insert_at_index(s: str, i: int, char: str) -> str:
    """
    Insert a character at a specific index in a string.
    Args:
        s: The string to insert the character into.
        i: The index to insert the character at.
        char: The character to insert.
    Returns:
        The string with the character inserted at the specified index.
    
    Examples:
    insert_at_index("Hello, world!", 5, "a") -> "Helloa, world!"
    """
    return s[:i] + char + s[i:]

def replace_at_range(s: str, start_pos: int, end_pos: int, new_char: str) -> str:
    """
    Replace a range of characters in a string with a new character.
    Args:
        s: The string to replace the characters in.
        start_pos: The start position of the range to replace.
        end_pos: The end position of the range to replace.
        new_char: The character to replace the range with.
    Returns:
        The string with the range replaced by the new character.

    Examples:
    replace_at_range("Hello, world!", 5, 7, "a") -> "Helloaworld!"
    """
    return s[:start_pos] + new_char + s[end_pos:]
